{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 企业级问答系统 —— 在线问答流程\n",
    "\n",
    "## 一、背景介绍\n",
    "\n",
    "智能问答系统是当前应用范围最广，最容易落地的大模型应用。通常来说，智能问答系统包括以下五部分：\n",
    "1. Query理解\n",
    "2. 混合检索召回\n",
    "3. 语义排序\n",
    "4. 答案生成\n",
    "5. 追问生成\n",
    "\n",
    "AppBuilder在问答系统沉淀了最佳实践流程，并以极易用的组件化的方式对外开放，可以使用页面操作 + 低代码的方式快速搭建自己的问答系统。\n",
    "\n",
    "<img src=\"./qa_system/qa_flow.png\" alt=\"drawing\" width=\"1000\"/>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二、实操流程\n",
    "\n",
    "推荐您先完成[企业级问答系统——离线知识生产流程](./qa_system_1_dataset.ipynb)的文档的学习，再进行本文的操作。\n",
    "\n",
    "\n",
    "### 2.1 前置操作简述\n",
    "\n",
    "- 【必须】登录[百度智能云千帆AppBuilder官网](https://cloud.baidu.com/product/AppBuilder)创建账户。\n",
    "- 【必须】在[百度智能云千帆AppBuilder控制台](https://console.bce.baidu.com/ai_apaas/dialogHome)左侧菜单栏『我的密钥』页面获取密钥，并复制。\n",
    "- 【必须】在python3.9及以上的环境中安装`appbuilder-sdk`\n",
    "- 【非必须】(可以在创建应用时在页面操作)通过SDK代码态创建知识库，并上传文档，参考[企业级问答系统——离线知识生产流程](./qa_system_1_dataset.ipynb)，"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2、创建应用\n",
    "\n",
    "\n",
    "##### 2.2.1、点击创建应用界面\n",
    "点击[百度智能云千帆AppBuilder控制台](https://console.bce.baidu.com/ai_apaas/dialogHome)左侧菜单栏『创建应用』，开始我们的创建知识问答系统应用。\n",
    "\n",
    "<img src=\"./qa_system/magic_create.jpg\" alt=\"drawing\" width=\"1000\"/>\n",
    "\n",
    "\n",
    "#### 2.2.2、填写应用信息，使用魔法棒自动生成instruction\n",
    "\n",
    "我们需要首先配置该应用的基本信息，包括 名称、描述、角色指令、开场白、推荐问、能力组件等。\n",
    "\n",
    "这类信息比较繁杂，存在一些填写技巧，填写的质量决定了后续应用的使用体验。\n",
    "\n",
    "AppBuilder 提供了『魔法棒』功能，可以自动生成instruction。用户只需要提供简单的描述，即可自动生成instruction。\n",
    "\n",
    "示例描述：`你是一个尽职尽责的客服问答助手。你有多个产品使用说明书，根据说明书上的内容回答用户的使用问题。`\n",
    "\n",
    "<img src=\"./qa_system/magic_instruction.jpg\" alt=\"drawing\" width=\"1000\"/>\n",
    "\n",
    "生成结果示例：\n",
    "\n",
    "<img src=\"./qa_system/app_preview.jpg\" alt=\"drawing\" width=\"1000\"/>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2.3、添加知识库\n",
    "\n",
    "在 『能力扩展』-『知识库』的操作栏中，我们添加在 [企业级问答系统——离线知识生产流程](./qa_system_1_dataset.ipynb) 中创建的 『说明书知识库』\n",
    "\n",
    "<img src=\"./qa_system/add_knowledge.jpg\" alt=\"drawing\" width=\"1000\"/>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2.4、页面对话并发布应用\n",
    "\n",
    "我们可以在页面右侧『预览与调试』对话栏中，通过对话进行快速调试，若符合预期，则点选右上角『发布』按钮发布该应用。\n",
    "\n",
    "<img src=\"./qa_system/web_chat.jpg\" alt=\"drawing\" width=\"1000\"/>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.2.5 获取已发布应用的ID\n",
    "\n",
    "在 [百度智能云千帆AppBuilder控制台-我的应用](https://console.bce.baidu.com/ai_apaas/app)页面中，可以查看已发布应用的ID，我们复制该ID，开始后续的代码态操作。\n",
    "\n",
    "<img src=\"./qa_system/get_app_id.jpg\" alt=\"drawing\" width=\"1000\"/>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 使用SDK调用已发布App\n",
    "\n",
    "当应用已经发布后，我们可以通过SDK在代码态调用，方便用户集成到自己的系统中，通过自己的系统对外提供服务。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.1 引入AppBuilder-SDK，设置TOKEN，设置APPID"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AppBuilder 模块导入成功！\n",
      "您的AppBuilder Token为：your api key\n",
      "您的AppBuilder App ID为：3df6c11e-44ec-438a-86a9-47b590e7cef5\n"
     ]
    }
   ],
   "source": [
    "# 引入os模块，引入appbuilder 模块\n",
    "import os\n",
    "import appbuilder\n",
    "\n",
    "# 设置appbuilder的token密钥，从页面上复制粘贴我的密钥，覆盖此处的 \"your_appbuilder_token\"\n",
    "os.environ['APPBUILDER_TOKEN'] = \"your_appbuilder_token\"\n",
    "# 设置需要调用的app，从页面上复制粘贴应用ID，覆盖此处的 \"your_publish_app_id\"\n",
    "app_id = \"your_publish_app_id\"\n",
    "\n",
    "print(\"AppBuilder 模块导入成功！\")\n",
    "print(\"您的AppBuilder Token为：{}\".format(os.environ['APPBUILDER_TOKEN']))\n",
    "print(\"您的AppBuilder App ID为：{}\".format(app_id))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.2 初始化Agent实例，创建会话并对话\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "智能客服问答助手的回复是：你好，作为客服问答助手，我可以为您解答关于多个产品的使用问题。如果您有任何疑问或需要帮助，请随时告诉我。\n"
     ]
    }
   ],
   "source": [
    "# 初始化Agent实例\n",
    "agent = appbuilder.AppBuilderClient(app_id)\n",
    "\n",
    "# 创建会话ID\n",
    "conversation_id = agent.create_conversation()\n",
    "\n",
    "# 发送并消息\n",
    "response_message = agent.run(\n",
    "    conversation_id = conversation_id,\n",
    "    query=\"你好，请问你可以为我做什么？\"\n",
    ")\n",
    "\n",
    "print(\"智能客服问答助手的回复是：{}\".format(response_message.content.answer))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.3 使用Stream模式，更快的刷新回复结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "流式输出回答：\n",
      "流式输出回答：\n",
      "流式输出回答：\n",
      "流式输出回答：显示器的放置\n",
      "流式输出回答：高度需要注意以下几点：\n",
      "\n",
      "1. **调整显示器高度以使眼睛和显示器中心的距离适合可视距离的习惯**^[1]^。\n",
      "流式输出回答：\n",
      "2. **如果您的显示器无法进行高度调整，则可能需要在显示器底座下放置几本书或其他坚固的物体以达到希望的高度**^[1]^。\n",
      "流式输出回答：\n",
      "3. **一般准则是调整显示器位置使屏幕顶部处于或略低于您在座位上感到舒适时视线的高度**^[1]^。\n",
      "流式输出回答：\n",
      "4. **务必优化您的显示器高度以使眼睛和显示器中心的距离适合可视距离的习惯，同时确保您在眼部肌肉处于放松状态时看屏幕感到\n",
      "流式输出回答：舒适**^[1]^。\n",
      "流式输出回答：\n",
      "流式输出回答：\n",
      "\n",
      "最终回答拼接结果：显示器的放置高度需要注意以下几点：\n",
      "\n",
      "1. **调整显示器高度以使眼睛和显示器中心的距离适合可视距离的习惯**^[1]^。\n",
      "2. **如果您的显示器无法进行高度调整，则可能需要在显示器底座下放置几本书或其他坚固的物体以达到希望的高度**^[1]^。\n",
      "3. **一般准则是调整显示器位置使屏幕顶部处于或略低于您在座位上感到舒适时视线的高度**^[1]^。\n",
      "4. **务必优化您的显示器高度以使眼睛和显示器中心的距离适合可视距离的习惯，同时确保您在眼部肌肉处于放松状态时看屏幕感到舒适**^[1]^。\n"
     ]
    }
   ],
   "source": [
    "# 使用Steam模式发送并接受消息\n",
    "response_message = agent.run(\n",
    "    conversation_id = conversation_id,\n",
    "    query=\"显示器的放置高度需要注意什么\",\n",
    "    stream=True\n",
    ")\n",
    "\n",
    "final_answer = \"\"\n",
    "for content in response_message.content:\n",
    "    final_answer += content.answer\n",
    "    print(\"流式输出回答：{}\".format(content.answer))\n",
    "\n",
    "print(\"\\n最终回答拼接结果：{}\".format(final_answer))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.4 多轮对话\n",
    "\n",
    "使用同一个conversation_id 即可进行多轮对话"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前的 ConversationID 为: 6c217eb2-d822-4b06-8f6c-d0e94f23dc09\n",
      "\n",
      "用户的第一轮问题是： 你好，请问电源指示灯未点亮是什么原因\n",
      "\n",
      "智能客服问答助手的回复是：你好，根据搜索结果，电源指示灯未点亮的可能原因包括：显示器的电源开关未打开、电源线松动或断开、插座没有电等^[1]^。\n",
      "\n",
      "用户的第二轮问题是： 我应该怎么操作解决该问题\n",
      "\n",
      "智能客服问答助手的回复是：如果显示器的电源指示灯未点亮，且没有图像，可能有以下几种原因：显示器的电源开关未打开、电源线松动或断开、插座没有电。因此，建议首先检查这些可能的原因，并采取相应的解决措施：确保电线正确连接、确保插座有电、打开显示器电源、尝试使用另一电源线、尝试使用另一电源插座^[1]^。\n",
      "\n"
     ]
    }
   ],
   "source": [
    "conversation_id = agent.create_conversation()\n",
    "print(\"当前的 ConversationID 为: {}\\n\".format(conversation_id))\n",
    "\n",
    "# 发送第一轮消息\n",
    "first_query = \"你好，请问电源指示灯未点亮是什么原因\"\n",
    "response_message = agent.run(\n",
    "    conversation_id = conversation_id,\n",
    "    query=first_query\n",
    ")\n",
    "print(\"用户的第一轮问题是： {}\\n\".format(first_query))\n",
    "print(\"智能客服问答助手的回复是：{}\\n\".format(response_message.content.answer))\n",
    "\n",
    "# 发送第二轮消息\n",
    "second_query = \"我应该怎么操作解决该问题\"\n",
    "response_message = agent.run(\n",
    "    conversation_id = conversation_id, # 继续复用上一轮对话ID\n",
    "    query=second_query\n",
    ")\n",
    "print(\"用户的第二轮问题是： {}\\n\".format(second_query))\n",
    "print(\"智能客服问答助手的回复是：{}\\n\".format(response_message.content.answer))\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.3.5 展示回答中的引用的文档\n",
    "\n",
    "回答中若引用了知识库中的文档，可以提供展示文档名功能，更置信的回答用户问题。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "智能客服问答助手的回复是：在Windows系统中安装显示器驱动程序的步骤如下：\n",
      "\n",
      "1. 关闭计算机和所有已连接设备的电源，确保显示器连接正确^[1]^。\n",
      "2. 打开显示器的电源，然后打开系统单元的电源，让系统启动进入Windows系统^[1]^。\n",
      "3. 打开显示属性窗口，单击开始、控制面板、硬件和声音图标，然后单击显示图标^[1][2]^。\n",
      "4. 单击更改显示设置选项卡，然后单击高级设置图标^[1][2]^。\n",
      "5. 单击显示器选项卡，然后单击属性按钮^[1][2]^。\n",
      "6. 单击驱动程序选项卡，然后单击更新驱动程序，选择浏览计算机以查找驱动程序^[1][2]^。\n",
      "7. 选择从计算机上的设备驱动程序列表中选择，然后单击从磁盘安装按钮，浏览到X:Windows 8目录（其中X是CD-ROM驱动器的盘符），选择\"LENLS2014wA.inf\"文件，然后单击打开按钮^[1]^。\n",
      "8. 单击确定按钮，完成安装^[1]^。\n",
      "\n",
      "以上就是安装显示器驱动程序的步骤，希望对你有所帮助。\n",
      "\n",
      "智能客服问答助手的回复中，角标 2 对应参考文档是：显示器说明书.pdf\n"
     ]
    }
   ],
   "source": [
    "response_message = agent.run(\n",
    "    conversation_id = conversation_id,\n",
    "    query=\"如何在windows系统中安装显示器驱动\",\n",
    ")\n",
    "\n",
    "print(\"智能客服问答助手的回复是：{}\\n\".format(response_message.content.answer))\n",
    "\n",
    "reference_doc_id = '2'\n",
    "reference_doc_name = \"\"\n",
    "for event in response_message.content.events:\n",
    "    if event.event_type == \"RAGAgent\" and 'references' in event.detail:\n",
    "        references = event.detail.get('references')\n",
    "        for reference_doc in references:\n",
    "            if reference_doc.get('id', '0') == reference_doc_id:\n",
    "                reference_doc_name = reference_doc.get('document_name', '')\n",
    "        break\n",
    "\n",
    "print(\"智能客服问答助手的回复中，角标 {} 对应参考文档是：{}\".format(reference_doc_id, reference_doc_name))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 三、总结\n",
    "\n",
    "AppBuilder 为 基于RAG的 企业问答系统提供了 `appbuilder.console.agentbuilder` API，可以方便的以代码态调用已在平台发布的对话应用，方便的集成进用户自己的系统。\n",
    "\n",
    "本示例展示了如何创建应用，代码态调用应用，非流式对话，流式对话，以及基于对话结果展示引用文档。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
